package com.heqian.clipimage;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;

/**
 * Created by Administrator on 2016/10/24.
 */

@SuppressLint("AppCompatCustomView")
public class ClipImageView extends android.widget.ImageView {
    /**
     * 圆形
     */
    public static final int TYPE_CIRCLE = 0;
    /**
     * 圆角矩形
     */
    public static final int TYPE_ROUNDED_RECT = 1;

    private int mType = TYPE_CIRCLE;
    private int mBorderColor = 0xffffffff;
    private float mBorderWidth = 4;
    private int mRectRoundRadius = 8;

    private int mClipWidth;
    private int mClipHeight;
    private int mBoxWidth = dip2px(10);
    private int mBoxColor1 = 0xffffffff;
    private int mBoxColor2 = 0xffa0a0a0;

    private Bitmap mBackground = null;
    private Bitmap mShadeBitmap = null;
    private Paint mBitmappaint = new Paint();

    private Matrix mMatrix = new Matrix();
    float scale = 1;
    private float x1, y1;
    private PointF drawPoint = new PointF();
    private boolean isScale = false;
    RectF mShadeRect = new RectF();
    private int mMaskColor = 0xE0000000;


    public ClipImageView(Context context) {
        super(context);
        mBitmappaint.setFlags(Paint.FILTER_BITMAP_FLAG);
    }


    public ClipImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        //取xml文件中设定的参数
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ClipImageView);
        mType = ta.getInt(R.styleable.ClipImageView_clipType, 0);
        mBorderColor = ta.getColor(R.styleable.ClipImageView_borderColor, 0xffffffff);
        mBorderWidth = ta.getDimensionPixelSize(R.styleable.ClipImageView_borderWidth, dip2px(2));
        mRectRoundRadius = ta.getDimensionPixelSize(R.styleable.ClipImageView_rectRadius, dip2px(8));
        mClipWidth = ta.getDimensionPixelSize(R.styleable.ClipImageView_clipWidth, dip2px(100));
        mClipHeight = ta.getDimensionPixelSize(R.styleable.ClipImageView_clipHeight, dip2px(100));

        mBoxWidth = ta.getDimensionPixelSize(R.styleable.ClipImageView_backgroundBoxWidth, dip2px(10));
        mBoxColor1 = ta.getColor(R.styleable.ClipImageView_backgroundBoxColor1, 0xffffffff);
        mBoxColor2 = ta.getColor(R.styleable.ClipImageView_backgroundBoxColor2, 0xffa0a0a0);
        mMaskColor = ta.getColor(R.styleable.ClipImageView_maskColor, 0xE0000000);
        ta.recycle();

        mBitmappaint.setFlags(Paint.FILTER_BITMAP_FLAG);
    }


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        createBackground();
        createShade();
        drawPoint.x = getWidth() / 2;
        drawPoint.y = getHeight() / 2;
    }

    private void createShade() {
        mShadeBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(mShadeBitmap);
        float borderWidth = mBorderWidth / 2;
        mShadeRect.bottom = getHeight() - getPaddingBottom() - borderWidth;
        mShadeRect.left = getPaddingLeft() + borderWidth;
        mShadeRect.right = getWidth() - getPaddingRight() - borderWidth;
        mShadeRect.top = getPaddingTop() + borderWidth;

        float scale = mShadeRect.height() / mClipHeight > mShadeRect.width() / mClipWidth ? mShadeRect.width() / mClipWidth : mShadeRect.height() / mClipHeight;
        float height = Math.max(0, (mShadeRect.height() - mClipHeight * scale) / 2);
        float width = Math.max(0, (mShadeRect.width() - mClipWidth * scale) / 2);
        mShadeRect.left += width;
        mShadeRect.right -= width;
        mShadeRect.top += height;
        mShadeRect.bottom -= height;


        Paint stroke = new Paint();
        stroke.setAntiAlias(true);
        stroke.setStyle(Paint.Style.STROKE);
        stroke.setColor(mBorderColor);
        stroke.setStrokeWidth(mBorderWidth);

        Paint fill = new Paint();
        fill.setAntiAlias(true);
        fill.setStyle(Paint.Style.FILL);
        fill.setColor(0xffffffff);
        PorterDuffXfermode mode = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);
        fill.setXfermode(mode);

        canvas.drawColor(mMaskColor);
        if (mType == TYPE_CIRCLE) {
            canvas.drawOval(mShadeRect, fill);
            canvas.drawOval(mShadeRect, stroke);

        } else if (mType == TYPE_ROUNDED_RECT) {
            float halfBorderWidth = mBorderWidth / 2.0f;
            float borderRadius = mRectRoundRadius - halfBorderWidth > 0.0f ? mRectRoundRadius - halfBorderWidth : 0.0f;
            float bitmapRadius = mRectRoundRadius - mBorderWidth > 0.0f ? mRectRoundRadius - mBorderWidth : 0.0f;
            canvas.drawRoundRect(mShadeRect, borderRadius, borderRadius, fill);
            canvas.drawRoundRect(mShadeRect, bitmapRadius, bitmapRadius, stroke);
        }
    }

    private void createBackground() {
        mBackground = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(mBackground);
        int height = getHeight() + mBoxWidth;
        int width = getWidth() + mBoxWidth;
        boolean boolX;
        boolean boolY = true;
        Paint mBoxPaint = new Paint();
        mBoxPaint.setStyle(Paint.Style.FILL);
        mBoxPaint.setColor(mBoxColor2);
        canvas.drawColor(mBoxColor1);
        for (int y = 0; y < height; y += mBoxWidth) {
            boolX = boolY;
            for (int x = 0; x < width; x += mBoxWidth) {
                if (boolX)
                    canvas.drawRect(x, y, x + mBoxWidth, y + mBoxWidth, mBoxPaint);
                boolX = !boolX;
            }
            boolY = !boolY;
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        Bitmap rawBitmap = getBitmap(getDrawable());
        canvas.drawBitmap(mBackground, 0, 0, null);

        if (rawBitmap != null) {
            float width = rawBitmap.getWidth();
            float height = rawBitmap.getHeight();
            float minScale = mShadeRect.width() / width < mShadeRect.height() / height ? mShadeRect.height() / height : mShadeRect.width() / width;
            if (scale < minScale) scale = minScale;
            width /= 2;
            height /= 2;
            mMatrix.setScale(scale, scale, width, height);
            if (drawPoint.x - width * scale > mShadeRect.left)
                drawPoint.x = mShadeRect.left + width * scale;

            if (drawPoint.x + width * scale < mShadeRect.right)
                drawPoint.x = mShadeRect.right - width * scale;

            if (drawPoint.y - height * scale > mShadeRect.top)
                drawPoint.y = mShadeRect.top + height * scale;

            if (drawPoint.y + height * scale < mShadeRect.bottom)
                drawPoint.y = mShadeRect.bottom - height * scale;

            mMatrix.postTranslate(drawPoint.x - width, drawPoint.y - height);
            canvas.drawBitmap(rawBitmap, mMatrix, null);
        }
        canvas.translate(0, 0);
        canvas.drawBitmap(mShadeBitmap, 0, 0, null);
    }

    private int dip2px(int dipVal) {
        float scale = getResources().getDisplayMetrics().density;
        return (int) (dipVal * scale + 0.5f);
    }

    private Bitmap getBitmap(Drawable drawable) {
        if (drawable instanceof BitmapDrawable) {
            return ((BitmapDrawable) drawable).getBitmap();
        } else {
            return null;
        }
    }

    ScaleGestureDetector gestureDetector = new ScaleGestureDetector(this.getContext(), new ScaleGestureDetector.OnScaleGestureListener() {
        float factor = 1;

        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            scale += detector.getScaleFactor() - factor;
            scale = Math.max(0.1f, Math.min(scale, 3.0f));
            factor = detector.getScaleFactor();
            invalidate();
            return false;
        }

        @Override
        public boolean onScaleBegin(ScaleGestureDetector detector) {
            factor = detector.getScaleFactor();
            isScale = true;
            return true;
        }

        @Override
        public void onScaleEnd(ScaleGestureDetector detector) {
            isScale = false;
        }
    });

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        gestureDetector.onTouchEvent(event);
        if (!isScale) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    this.x1 = event.getRawX();
                    this.y1 = event.getRawY();

                    break;
                case MotionEvent.ACTION_MOVE:
                    drawPoint.x += event.getRawX() - x1;
                    drawPoint.y += event.getRawY() - y1;
                    x1 = event.getRawX();
                    y1 = event.getRawY();
                    invalidate();
            }
        }
        return true;
    }

    public Bitmap getClipImage() {
        Bitmap ret = null;
        Bitmap rawBitmap = getBitmap(getDrawable());
        if (0 < mClipWidth && 0 < mClipHeight && null != rawBitmap) {
            float width = rawBitmap.getWidth();
            float height = rawBitmap.getHeight();
            float clipScale = mClipWidth / mShadeRect.width();

            ret = Bitmap.createBitmap(mClipWidth, mClipHeight, Bitmap.Config.ARGB_8888);
            Canvas canvas = new Canvas(ret);

            width /= 2;
            height /= 2;

            Matrix matrix = new Matrix();
            matrix.setScale(scale * clipScale, scale * clipScale);
            matrix.postTranslate((drawPoint.x - scale * width - mShadeRect.left) * clipScale, (drawPoint.y - scale * height - mShadeRect.top) * clipScale);
            canvas.drawBitmap(rawBitmap, matrix, null);
        }
        return ret;
    }

    @Override
    public void setImageBitmap(Bitmap bm) {
        int width = bm.getWidth();
        int height = bm.getHeight();
        float scale = 1;
        if (2048 < width || 2048 < height) {
            Paint paint = new Paint();
            paint.setFlags(Paint.FILTER_BITMAP_FLAG);
            scale = width > height ? 2048f / height : 2048f / width;
            Bitmap ret = Bitmap.createBitmap((int) (scale * width), (int) (scale * height), Bitmap.Config.ARGB_8888);
            Canvas canvas = new Canvas(ret);
            canvas.scale(scale, scale);
            canvas.drawBitmap(bm, 0, 0, paint);
            super.setImageBitmap(ret);
        } else
            super.setImageBitmap(bm);
    }

    public int getType() {
        return mType;
    }

    public void setmType(int mType) {
        this.mType = mType;
    }

    public int getBorderColor() {
        return mBorderColor;
    }

    public void setmBorderColor(int mBorderColor) {
        this.mBorderColor = mBorderColor;
    }

    public float getBorderWidth() {
        return mBorderWidth;
    }

    public void setmBorderWidth(float mBorderWidth) {
        this.mBorderWidth = mBorderWidth;
    }

    public int getRectRoundRadius() {
        return mRectRoundRadius;
    }

    public void setmRectRoundRadius(int mRectRoundRadius) {
        this.mRectRoundRadius = mRectRoundRadius;
    }

    public int getClipWidth() {
        return mClipWidth;
    }

    public void setmClipWidth(int mClipWidth) {
        this.mClipWidth = mClipWidth;
    }

    public int getClipHeight() {
        return mClipHeight;
    }

    public void setmClipHeight(int mClipHeight) {
        this.mClipHeight = mClipHeight;
    }

    public int getBoxWidth() {
        return mBoxWidth;
    }

    public void setmBoxWidth(int mBoxWidth) {
        this.mBoxWidth = mBoxWidth;
    }

    public int getBoxColor1() {
        return mBoxColor1;
    }

    public void setmBoxColor1(int mBoxColor1) {
        this.mBoxColor1 = mBoxColor1;
    }

    public int getBoxColor2() {
        return mBoxColor2;
    }

    public void setmBoxColor2(int mBoxColor2) {
        this.mBoxColor2 = mBoxColor2;
    }

    public int getmMaskColor() {
        return mMaskColor;
    }

    public void setmMaskColor(int mMaskColor) {
        this.mMaskColor = mMaskColor;
    }
}